#include <gtest/gtest.h>

//OK 1. action ddl 
//OK 2. execute dml
//OK 3. query: fetch, fetch_row, fetch_values
//OK 4. queryScalar : query one value

#include <memory>

#include "helper/unittest/Tracer.h"

#include "helper/sqlite/Statement.h"
#include "helper/sqlite/QueryResult.h"

#include "helper/sqlite/shortcut/Action.h"
#include "helper/sqlite/shortcut/Executor.h"
#include "helper/sqlite/shortcut/Query.h"
#include "helper/sqlite/shortcut/QueryScalar.h"

#include "MemoryDBTest.h"

class SqliteShortcutTest : public test::EmptyMemoryDBTest{
};

TEST_F(SqliteShortcutTest, all_functions){
  using namespace helper::sqlite;

  {
    shortcut::Action action(db()); 

    const char * sql = "create table if not exists tab1 (one text primary key, two int);";
    ActionResult && r = action.action(sql);

    ASSERT_TRUE(r);
  }

  {
    shortcut::Executor exe(db());

    const char * sql = "insert into tab1 values ('1111', 23);";
    ExecutionResult && r = exe.execute(sql);

    ASSERT_TRUE(r);
    ASSERT_EQ(1, r.affectRows());
  }
  {
    shortcut::Executor exe(db());

    const char * sql = "insert into tab1 values (?, ?);";

    std::string text("3333"); //alive in executing
    int iv =  44;
    ExecutionResult && r = exe.execute(sql
        , [&text, iv](auto & stmt) {
        if(auto && ec = stmt.textBind(1, text); ec) return ec;
        if(auto && ec = stmt.intBind(2, iv); ec) return ec;
        return std::error_code();
        }
        );

    ASSERT_TRUE(r);
    ASSERT_EQ(1, r.affectRows());
  }


  {  //query::Query
    shortcut::Query q(db());

    auto && ffetch = [](auto & r){
      int rowid = 0;
      while(r){
        int cc = r.columnCount();
        for(int i = 0; i < cc; ++i){
          TRACER.operation(std::to_string(rowid) + "-" + std::to_string(i) + "-" + r.values(i)->string());
        }
        ++rowid;
        r.next();
      }
      return std::error_code();
    };

    { //query::Query no-fbind
      EXPECT.reset()
        .operation("0-0-1111")
        .operation("0-1-23")
        .operation("1-0-3333")
        .operation("1-1-44")
        ;
      TRACER.reset();
      const char * sql = "select * from tab1";
      auto && ec = q.query(sql, ffetch);
      ASSERT_EQ(std::error_code(), ec);
      ASSERT_EQ(EXPECT, TRACER);
    }

    {//query::Query with fbind
      EXPECT.reset()
        .operation("0-0-3333")
        .operation("0-1-44")
        ;
      TRACER.reset();
      const char * sql = "select * from tab1 where one >?;";
      const std::string text("1111");
      auto && fbind = [&text](auto & stmt){
        if(auto && ec = stmt.textBind(1, text); ec){
          return ec;
        }
        return std::error_code();
      };
      auto && ec = q.query(sql, ffetch, fbind);
      ASSERT_EQ(std::error_code(), ec);
      ASSERT_EQ(EXPECT, TRACER);
    }
    { // query::Query. foreach-row
      EXPECT.reset()
        .operation("0-0-1111")
        .operation("0-1-23")
        .operation("1-0-3333")
        .operation("1-1-44")
        ;

      TRACER.reset();
      auto && fvisit_row = [](auto rowid, auto & r){
        for(int i = 0; i < r.columnCount(); ++i){
          TRACER.operation(std::to_string(rowid) + "-" + std::to_string(i) + "-" +r.values(i)->string());
        }
        return std::error_code();
      };
      const char * sql = "select * from tab1";
      auto && ec = q.query(sql, fvisit_row);
      ASSERT_EQ(std::error_code(), ec);

      ASSERT_EQ(EXPECT, TRACER);
    }
    { // query::Query. for-each value
      EXPECT.reset()
        .operation("0-0-1111")
        .operation("0-1-23")
        .operation("1-0-3333")
        .operation("1-1-44")
        ;
      TRACER.reset();
      auto && fvisit_value = [](int rowid, int colid, std::optional<Value> && value, QueryResult & r){
        TRACER.operation(std::to_string(rowid) + "-" + std::to_string(colid) + "-" + value->string());
        return std::error_code();
      };

      const char * sql = "select * from tab1";
      auto && ec = q.query(sql, fvisit_value);
      ASSERT_EQ(std::error_code(), ec);
      ASSERT_EQ(EXPECT, TRACER);
    }
  }

  { //query scalar
    shortcut::QueryScalar scalar(db());
    const std::string sql = "select count(1) from tab1";
    {
      int v = 0;
      auto && ec = scalar.queryInteger(sql, v);
      ASSERT_EQ(2, v);
      ASSERT_EQ(std::error_code(), ec);
    }
    {
      double v = 0.0;
      auto && ec = scalar.queryReal(sql, v);
      ASSERT_EQ(2.0, v);
      ASSERT_EQ(std::error_code(), ec);
    }
    {
      std::string v;
      auto && ec = scalar.queryText(sql, v);
      ASSERT_EQ("2", v);
      ASSERT_EQ(std::error_code(), ec);
    }
    {
      std::string v;
      auto && ec = scalar.queryBinary(sql, v);
      ASSERT_EQ("2", v);
      ASSERT_EQ(std::error_code(), ec);

    }
  }
}

